package tokenverify

import (
	"errors"
	"github.com/golang-jwt/jwt/v4"
	"jinrigaoping/pkg/constant"
	"jinrigaoping/pkg/xcode"
	"time"
)

const (
	TokenUser       = constant.NormalUser
	TokenAdmin      = constant.AdminUser
	TokenSuperAdmin = constant.SuperAdminUser
)

type claims struct {
	UserId     int64
	UserType   int32
	PlatformId int32
	MerchantId int64
	jwt.RegisteredClaims
}

type TokenUserInfo struct {
	UserId     int64
	UserType   int32
	PlatformId int32
	MerchantId int64
}

func buildClaims(tokenUserInfo *TokenUserInfo, ttl int64) claims {
	now := time.Now()
	before := now.Add(-time.Minute * 5)
	return claims{
		UserId:     tokenUserInfo.UserId,
		UserType:   tokenUserInfo.UserType,
		MerchantId: tokenUserInfo.MerchantId,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(now.Add(time.Duration(ttl*24) * time.Hour)), // Expiration time
			IssuedAt:  jwt.NewNumericDate(now),                                        // Issuing time
			NotBefore: jwt.NewNumericDate(before),                                     // Begin Effective time
		},
	}
}

func CreateToken(tokenUserInfo *TokenUserInfo, ttl int64, secret string) (string, error) {

	if tokenUserInfo == nil {
		return "", xcode.TokenUnknown
	}

	if !(tokenUserInfo.UserType == TokenUser || tokenUserInfo.UserType == TokenAdmin || tokenUserInfo.UserType == TokenSuperAdmin) {
		return "", xcode.TokenUnknown
	}
	claims := buildClaims(tokenUserInfo, ttl)
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString([]byte(secret))
	if err != nil {
		return "", err
	}
	return tokenString, nil
}

func getToken(t string, secret string) (*TokenUserInfo, error) {
	token, err := jwt.ParseWithClaims(t, &claims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(secret), nil
	})
	if err != nil {
		var ve *jwt.ValidationError
		if errors.As(err, &ve) {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return &TokenUserInfo{}, xcode.ErrTokenMalformed
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				return &TokenUserInfo{}, xcode.ValidationErrorExpired
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return &TokenUserInfo{}, xcode.ValidationErrorNotValidYet
			} else {
				return &TokenUserInfo{}, xcode.TokenUnknown
			}
		} else {
			return &TokenUserInfo{}, xcode.TokenUnknown
		}
	} else {
		claims, ok := token.Claims.(*claims)
		if claims.PlatformId != 0 {
			return &TokenUserInfo{}, xcode.ErrTokenNotExist
		}
		if ok && token.Valid {
			return &TokenUserInfo{
				UserId:     claims.UserId,
				UserType:   claims.UserType,
				PlatformId: claims.PlatformId,
				MerchantId: claims.MerchantId,
			}, nil
		}
		return &TokenUserInfo{}, xcode.ErrTokenInvalid
	}
}

func GetToken(token string, secret string) (*TokenUserInfo, error) {
	tokenUserInfo, err := getToken(token, secret)
	if err != nil {
		return &TokenUserInfo{}, err
	}

	if !(tokenUserInfo.UserType == TokenUser || tokenUserInfo.UserType == TokenAdmin || tokenUserInfo.UserType == TokenSuperAdmin) {
		return &TokenUserInfo{}, xcode.TokenUnknown
	}
	return tokenUserInfo, nil
}

func GetAdminToken(token string, secret string) (*TokenUserInfo, error) {
	tokenUserInfo, err := getToken(token, secret)
	if err != nil {
		return &TokenUserInfo{}, err
	}
	if tokenUserInfo.UserType != TokenAdmin {
		return &TokenUserInfo{}, xcode.ErrTokenInvalid
	}
	return tokenUserInfo, nil
}

func GetUserToken(token string, secret string) (*TokenUserInfo, error) {
	tokenUserInfo, err := getToken(token, secret)
	if err != nil {
		return &TokenUserInfo{}, err
	}
	if tokenUserInfo.UserType != TokenUser {
		return &TokenUserInfo{}, xcode.ErrTokenInvalid
	}
	return tokenUserInfo, nil
}

func GetSuperAdminToken(token string, secret string) (*TokenUserInfo, error) {
	tokenUserInfo, err := getToken(token, secret)
	if err != nil {
		return &TokenUserInfo{}, err
	}
	if tokenUserInfo.UserType != TokenSuperAdmin {
		return &TokenUserInfo{}, xcode.ErrTokenInvalid
	}
	return tokenUserInfo, nil
}
